# TODO - automatyczne odpalanie interfejsu WEB

import cfg
import cnd
from job import Job,all_job_names
import log
import tag

from Queue import Queue,Empty
import os
import time

class Scheduler:
	def __init__(self,loglevel=log.INFO):
		self.jobs = {} # job,date -> job_obj
		self.signal_queue = Queue()
		self.logger = None
		self.loglevel = loglevel # TODO - load from cfg
		self.reset_logger()

	def main(self,timeout=1.0):
		for j,d in self.runable_jobs():
			job = self.jobs[j,d]
			if job.should_run():
				job.run()
		try:
			j,d = self.signal_queue.get(True,timeout)
			job = self.jobs[j,d]
			if job.is_ok():
				del self.jobs[j,d]
		except Empty:
			return False
		return True

	def load_job(self,name,date,wait_for=[]):
		job = Job(name,date,self.signal_queue)
		if wait_for:
			job.wait_for = wait_for
		self.jobs[(name,date)] = job
		self.log(log.GARBAGE,"load_job %s %s",name,date)

	def runable_jobs(self):
		pjd = [(job.priority,job.name,job.date) for job in self.jobs.values() if job.should_run()]
		pjd.sort()
		return [(j,d) for p,j,d in pjd]

	def status(self,date):
		jobs = [j for j,d in self.jobs if d==date]
		return jobs

	def run(self,cnt=0):
		import time
		self.log(log.INFO,"scheduler started")
		while True:
			try:
				self.reload_jobs()
			except Exception,e:
				self.log(log.ERROR,"self.reload_jobs:",e)
			for i in range(3):
				print 'env', cfg.get('cfg','meta').get('name','Rivulet') # xxx
				print "jobs", self.jobs # xxx
				try:
					self.main(5)
				except Exception,e:
					self.log(log.ERROR,"self.main:",e)
			self.reset_logger()
			if cnt>1:
				cnt -= 1
			elif cnt==1:
				break

	def reload_jobs(self):
		import res
		try:
			res.create_all()
		except Exception,e:
			self.log(log.ERROR,"reload_jobs - cannot create all resources: %s",e)
		try:
			tag.reload()
		except Exception,e:
			self.log(log.ERROR,"reload_jobs - cannot reload tags: %s",e)
		for date,jobs in self.to_run_all().items():
			for name in cnd.list(date,type='forceok',strip=True):
				cnd.clear(name+'.forceok',date)
				cnd.add(name+'.ok',date)
				cnd.clear(name+'.run',date)
				cnd.clear(name+'.error',date)
				cnd.clear(name+'.sleep',date)
				cnd.add(name+'.ignore',date)
			for name in cnd.list(date,type='cycle',strip=True):
				if cnd.exists(name+'.run',date):
					if cnd.exists(name+'.ok',date) or cnd.exists(name+'.error',date):
						j = Job(name,date,fake=True)
						if j.cycle>=0:
							delta = time.time() - cnd.time_mod(name+'.run',date)
							if delta>=j.cycle*60:
								cnd.clear(name+'.run',date)
								cnd.clear(name+'.ok',date)
								cnd.clear(name+'.error',date)
								cnd.clear(name+'.sleep',date)
			for name in cnd.list(date,type='retry',strip=True):
				if cnd.exists(name+'.run',date) and (cnd.exists(name+'.error',date) or cnd.exists(name+'.sleep',date)):
					j = Job(name,date,fake=True)
					if cnd.exists(name+'.sleep',date):
						delta = time.time() - cnd.time_mod(name+'.sleep',date)
					else:
						delta = time.time() - cnd.time_mod(name+'.error',date)
					if delta>=max(1,j.retry)*60:
						cnd.clear(name+'.run',date)
						cnd.clear(name+'.error',date)
						cnd.clear(name+'.sleep',date)
			for name in cnd.list(date,type='forcerestart',strip=True):
				cnd.clear(name+'.forcerestart',date)
				cnd.clear(name+'.continue',date)
				cnd.add(name+'.force',date)
				cnd.add(name+'.test.force',date)
				cnd.add(name+'.rerun',date)
				cnd.add(name+'.order',date)
			for name in cnd.list(date,type='rerun',strip=True):
				cnd.clear(name+'.rerun',date)
				cnd.clear(name+'.run',date)
				cnd.clear(name+'.ok',date)
				cnd.clear(name+'.error',date)
				cnd.clear(name+'.sleep',date)
			for name in cnd.list(date,type='unstop',strip=True):
				cnd.clear(name+'.unstop',date)
				cnd.clear(name+'.stop',date)
			for name in cnd.list(date,type='restart',strip=True):
				cnd.clear(name+'.continue',date)
				cnd.clear(name+'.restart',date)
				cnd.clear(name+'.run',date)
				cnd.clear(name+'.ok',date)
				cnd.clear(name+'.error',date)
				cnd.clear(name+'.sleep',date)
			for name in cnd.list(date,type='unlock',strip=True):
				cnd.clear(name+'.unlock',date)
				res.unlock_all(name,date)
		for date,jobs in self.to_run_all().items():
			for name in jobs:
				try:
					self.load_job(name,date)
				except Exception,e:
					self.log(log.ERROR,"reload_jobs - cannot load job %s %s",name,date)
		self.log(log.SPAM,"reload_jobs %s",self.jobs.keys())

	def to_run_all(self):
		jobs = {}
		print "Scheduler dates", cnd.dates() # xxx
		self.log(log.GARBAGE,"cnd.dates %s", cnd.dates())
		for date in cnd.dates():
			print date # xxx
			jobs[date] = self.to_run(date)
		return jobs

	def to_run(self,date):
		order = cnd.list(date,type='order',strip=True)
		ok = cnd.list(date,type='ok',strip=True)
		error = cnd.list(date,type='error',strip=True)
		cycle = cnd.list(date,type='cycle',strip=True)
		return list(set(order)-((set(ok)|set(error))-set(cycle)))

	def reset_logger(self):
		self.logger = log.get_sch_logger(self.loglevel)

	def log(self,level,msg,*args,**kwargs):
		self.logger.log(level,msg,*args,**kwargs)

def order_all(date):
	out = []
	for name in all_job_names():
		try:
			job = Job(name,date,fake=True)
		except Exception,e:
			print e # TODO
			continue
		if job.should_be_ordered(date): 
			cnd.add(name+".order",date)
			out += [name]
			if job.cycle>0:
				cnd.add(name+".cycle",date)
			if job.retry>0:
				cnd.add(name+".retry",date)
			if job.cont>0:
				cnd.add(name+".continue",date)
	return out

def order(date,jobs,rerun=False):
	if type(jobs)==type(""):
		jobs=[jobs]
	for name in jobs:
		if rerun:
			cnd.clear_all(date,name+".*")
		cnd.add(name+".order",date)
		job = Job(name,date,fake=True)
		if job.cycle>0:
			cnd.add(name+".cycle",date)
		if job.retry>0:
			cnd.add(name+".retry",date)
		if job.cont>0:
			cnd.add(name+".continue",date)

if __name__=="__main__":
	s=Scheduler(log.GARBAGE)
	run_profiler=False
	if run_profiler:
		import profile
		import pstats
		profile.run("s.run(1)","rivulet.profile.stats")
		stats = pstats.Stats("rivulet.profile.stats")
		stats.sort_stats('cumulative')
		stats.print_stats(.2)
		stats.sort_stats('time')
		stats.print_stats(.2)
	else:
		s.run()
	#print order_all('20111025')
	#cnd.clear_all('20111025')
	#s=Scheduler()
	#print "all_job_names", all_job_names()
	#print "order_all", s.order_all('20111025')
	#print "jobs", s.jobs
	#print "wait_for", s.wait_for
	#print "status",s.status('20111025')
	#~ s.order_job('zzz','20110831',[('xxx.ok','20110831'),('yyy.ok','20110831')])
	#~ s.order_job('yyy','20110831',[('xxx.ok','20110831')])
	#~ s.order_job('xxx','20110831')
	#~ s.main()
	#~ s.main()
	#~ s.main()
	#~ s.order_all('20110105')
	#~ print s.jobs
	#~ print "ok"

